package Engine.Model
{
	import Engine.Manager.EngineManager;
	
	import flash.errors.EOFError;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import flash.utils.getQualifiedClassName;
	
	import library.SocketData.SocketData;
	
	
	public class GameSocket extends Socket
	{
		
		private static const MIN_PACK_HEAD_SIZE:int   = 4; //最小包头
		
		private var _socketName:String = "";
		private var _connectedFun:Function = null;
		private var _disConnectedFun:Function = null;
		
		private var recvBytes:ByteArray = new ByteArray();
		
		public function GameSocket(_name:String)
		{
			super(null, 0);
			
			this._socketName = _name;
			
			this.recvBytes.clear();
			this.recvBytes.endian = Endian.LITTLE_ENDIAN;
			
			this.configEventListenfun();
			
		}
		
		private function configEventListenfun():void
		{
			this.addEventListener(IOErrorEvent.IO_ERROR,ioErrHandler);
			this.addEventListener(SecurityErrorEvent.SECURITY_ERROR,securityErrHandler);
			this.addEventListener(Event.CONNECT,connectHandler);
			this.addEventListener(Event.CLOSE,closesockethandler);
			this.addEventListener(ProgressEvent.SOCKET_DATA,receiveMessageData);
			
		}
		
		private function ioErrHandler(event:IOErrorEvent ):void
		{
			
		}
		
		private function securityErrHandler(event:SecurityErrorEvent ):void
		{
			
		}
		
		private function connectHandler(event:Event):void
		{
			EngineManager.logPrint.printLog(_socketName,"Connected Success");
			if(this._connectedFun != null)
				this._connectedFun();
		}
		
		private function closesockethandler(event:Event):void
		{
			EngineManager.logPrint.printLog(_socketName,"The Socket Disconnected");
			this.recvBytes.clear();
			if (this._disConnectedFun != null)
				this._disConnectedFun();
		}
		
		private function receiveMessageData(event:ProgressEvent):void
		{
			if(!this.bytesAvailable)
				return;
			if(this.recvBytes.length>0)
			{
				this.recvBytes.position = this.recvBytes.length;
				this.readBytes(this.recvBytes);
			}
			else
			{
				this.readBytes(this.recvBytes);
			}
			if(this.recvBytes.length < MIN_PACK_HEAD_SIZE)
				return;
			var packsize:int=0;
			var packID:int = 0;
			var packData:ByteArray = new ByteArray();
			packData.endian = Endian.LITTLE_ENDIAN;
			while(this.recvBytes.length > 0)
			{
				if(this.recvBytes.length < MIN_PACK_HEAD_SIZE)
					return;
				this.recvBytes.position = 0;
				packsize = this.recvBytes.readShort();
				if(packsize < 0)
				{
					this.recvBytes.clear();
					EngineManager.logPrint.printLog(_socketName,"receive message length error",MIN_PACK_HEAD_SIZE);
					return;
				}
				
				packsize = packsize + MIN_PACK_HEAD_SIZE;
				
				if(this.recvBytes.length < packsize)
					return;
				packID = this.recvBytes.readShort();
				packData.clear();
				if(packsize > MIN_PACK_HEAD_SIZE)
					packData.writeBytes(this.recvBytes,MIN_PACK_HEAD_SIZE,packsize - MIN_PACK_HEAD_SIZE);
				
				this.handleMessage(packID,packData);
				
				if(this.recvBytes.length > packsize)
				{
					var tmpBytes:ByteArray = new ByteArray;
					tmpBytes.endian = Endian.LITTLE_ENDIAN;
					tmpBytes.writeBytes(this.recvBytes,packsize);
					this.recvBytes = tmpBytes;
					this.recvBytes.position = 0;
				}
				else
				{
					this.recvBytes.clear();
					return;
				}
			}
		}
		
		private function handleMessage(packID:int,packData:ByteArray):void
		{
			EngineManager.logPrint.printLog(_socketName,"Receive Message packID:"+String(packID)+" packLength:"+packData.length.toString());
			if(LogPrint.DEBUG)
				EngineManager.eventMgr.throwEvent(packID,new SocketData(packData));
			else{
				try{
					EngineManager.eventMgr.throwEvent(packID,new SocketData(packData));
				}
				catch(e:Error)
				{
					EngineManager.logPrint.printLog(_socketName,"Handle Message error Message ID:",packID);
					EngineManager.logPrint.printLog(_socketName,e.getStackTrace());
				}
			}
		}
		
		public function sendMessageData(msgID:int,msgData:*):void
		{
			if(!this.connected)
				return;
			
			var sendBytes:SocketData = new SocketData();
			sendBytes.endian = Endian.LITTLE_ENDIAN;
			
			if(msgData != null)
			{
				switch(getQualifiedClassName(msgData))
				{
					case "int":
						sendBytes.writeInt16(msgData);
						break;
					case "Number":
						sendBytes.writeFloat(msgData);
						break;
					case "String":
						sendBytes.writeString(msgData);
						break;
					case "library.SocketData::SocketData":
						sendBytes = msgData as SocketData;
						break;
					case "flash.utils::ByteArray":
						sendBytes = new SocketData(msgData);
						break;
					default:
						EngineManager.logPrint.printLog(_socketName,"haven't send Type:"+msgID.toString()+" Value:" + String(msgData)+"DateType not support",getQualifiedClassName(msgData));
						return; 
				}
			}
			
			var length:int = sendBytes.length;
			var data:ByteArray = new ByteArray();
			data.endian = Endian.LITTLE_ENDIAN ;
			data.writeShort(length);
			data.writeShort(msgID);
			data.writeBytes(sendBytes, 0, sendBytes.length);
			
			data.position = 0;
			
			this.writeBytes(data,0,data.length);
			this.flush();
			
			EngineManager.logPrint.printLog(_socketName,"Send Message msgID:",msgID,"length:",data.length);
		}
		
		public function connectSocket(ip:String,port:int):void
		{
			
			this.connect(ip,port);
			EngineManager.logPrint.printLog(_socketName,"Connect:",ip,port);
		}
		
		public function setConnectFun(fun:Function):void
		{
			this._connectedFun = fun;
		}
		
		public function setDisConnectFun(fun:Function):void
		{
			this._disConnectedFun = fun;
		}
		
		public function disconnected():void
		{
			if(!this.connected)
				return;
			this.close();
		}
		
	}
}